FlatKitFog.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. using UnityEngine;
  2. using UnityEngine.Rendering.Universal;
  3. #if UNITY_2022_3_OR_NEWER
  4. using ExternPropertyAttributes;
  5. namespace FlatKit {
  6. public class FlatKitFog : ScriptableRendererFeature {
  7. [Tooltip("To create new settings use 'Create > FlatKit > Fog Settings'.")]
  8. [Expandable]
  9. public FogSettings settings;
  10. private Material _effectMaterial;
  11. private DustyroomRenderPass _fullScreenPass;
  12. private bool _requiresColor;
  13. private bool _injectedBeforeTransparents;
  14. private ScriptableRenderPassInput _requirements = ScriptableRenderPassInput.Color;
  15. private Texture2D _lutDepth;
  16. private Texture2D _lutHeight;
  17. private const string ShaderName = "Hidden/FlatKit/FogWrap";
  18. private const string CameraRelativePosition = "FOG_CAMERA_RELATIVE";
  19. private const string UseDistanceFog = "USE_DISTANCE_FOG";
  20. private const string UseHeightFog = "USE_HEIGHT_FOG";
  21. private static int distanceLut => Shader.PropertyToID("_DistanceLUT");
  22. private static int near => Shader.PropertyToID("_Near");
  23. private static int far => Shader.PropertyToID("_Far");
  24. private static int distanceFogIntensity => Shader.PropertyToID("_DistanceFogIntensity");
  25. private static int heightLut => Shader.PropertyToID("_HeightLUT");
  26. private static int lowWorldY => Shader.PropertyToID("_LowWorldY");
  27. private static int highWorldY => Shader.PropertyToID("_HighWorldY");
  28. private static int heightFogIntensity => Shader.PropertyToID("_HeightFogIntensity");
  29. private static int distanceHeightBlend => Shader.PropertyToID("_DistanceHeightBlend");
  30. public override void Create() {
  31. // Settings.
  32. {
  33. if (settings == null) return;
  34. settings.onSettingsChanged = null;
  35. settings.onReset = null;
  36. settings.onSettingsChanged += SetMaterialProperties;
  37. settings.onReset += SetMaterialProperties;
  38. }
  39. // Material.
  40. {
  41. #if UNITY_EDITOR
  42. settings.effectMaterial = SubAssetMaterial.GetOrCreate(settings, ShaderName);
  43. if (settings.effectMaterial == null) return;
  44. #endif
  45. _effectMaterial = settings.effectMaterial;
  46. SetMaterialProperties();
  47. }
  48. {
  49. _fullScreenPass = new DustyroomRenderPass {
  50. renderPassEvent = settings.renderEvent,
  51. };
  52. _requirements = ScriptableRenderPassInput.Depth | ScriptableRenderPassInput.Color;
  53. ScriptableRenderPassInput modifiedRequirements = _requirements;
  54. _requiresColor = (_requirements & ScriptableRenderPassInput.Color) != 0;
  55. _injectedBeforeTransparents = settings.renderEvent <= RenderPassEvent.BeforeRenderingTransparents;
  56. if (_requiresColor && !_injectedBeforeTransparents) {
  57. modifiedRequirements ^= ScriptableRenderPassInput.Color;
  58. }
  59. _fullScreenPass.ConfigureInput(modifiedRequirements);
  60. }
  61. }
  62. public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) {
  63. if (settings == null || !settings.applyInSceneView && renderingData.cameraData.isSceneViewCamera) return;
  64. if (renderingData.cameraData.isPreviewCamera) return;
  65. if (_effectMaterial == null) return;
  66. _fullScreenPass.Setup(_effectMaterial, _requiresColor, _injectedBeforeTransparents, "Flat Kit Fog",
  67. renderingData);
  68. renderer.EnqueuePass(_fullScreenPass);
  69. }
  70. // Re-generate LUT textures when unity disposes them on scene save.
  71. #if UNITY_EDITOR
  72. public override void OnCameraPreCull(ScriptableRenderer renderer, in CameraData cameraData) {
  73. base.OnCameraPreCull(renderer, in cameraData);
  74. if (settings == null) return;
  75. if (settings.useDistance && !_effectMaterial.GetTexture(distanceLut)) UpdateDistanceLut();
  76. if (settings.useHeight && !_effectMaterial.GetTexture(heightLut)) UpdateHeightLut();
  77. }
  78. #endif
  79. protected override void Dispose(bool disposing) {
  80. _fullScreenPass?.Dispose();
  81. }
  82. private void SetMaterialProperties() {
  83. if (_effectMaterial == null) return;
  84. RendererFeatureUtils.SetKeyword(_effectMaterial, UseDistanceFog, settings.useDistance);
  85. if (settings.useDistance) {
  86. UpdateDistanceLut();
  87. _effectMaterial.SetFloat(near, settings.near);
  88. _effectMaterial.SetFloat(far, settings.far);
  89. _effectMaterial.SetFloat(distanceFogIntensity, settings.distanceFogIntensity);
  90. }
  91. RendererFeatureUtils.SetKeyword(_effectMaterial, UseHeightFog, settings.useHeight);
  92. if (settings.useHeight) {
  93. UpdateHeightLut();
  94. _effectMaterial.SetFloat(lowWorldY, settings.low);
  95. _effectMaterial.SetFloat(highWorldY, settings.high);
  96. _effectMaterial.SetFloat(heightFogIntensity, settings.heightFogIntensity);
  97. _effectMaterial.SetFloat(distanceHeightBlend, settings.distanceHeightBlend);
  98. }
  99. RendererFeatureUtils.SetKeyword(_effectMaterial, CameraRelativePosition, settings.cameraRelativePosition);
  100. }
  101. private void UpdateDistanceLut() {
  102. if (settings.distanceGradient == null) return;
  103. const int width = 256;
  104. const int height = 1;
  105. if (_lutDepth == null) {
  106. _lutDepth = new Texture2D(width, height, TextureFormat.RGBA32, /*mipChain=*/false) {
  107. wrapMode = TextureWrapMode.Clamp,
  108. hideFlags = HideFlags.HideAndDontSave,
  109. filterMode = FilterMode.Bilinear
  110. };
  111. }
  112. for (float x = 0; x < width; x++) {
  113. Color color = settings.distanceGradient.Evaluate(x / (width - 1));
  114. for (float y = 0; y < height; y++) {
  115. _lutDepth.SetPixel(Mathf.CeilToInt(x), Mathf.CeilToInt(y), color);
  116. }
  117. }
  118. _lutDepth.Apply();
  119. _effectMaterial.SetTexture(distanceLut, _lutDepth);
  120. }
  121. private void UpdateHeightLut() {
  122. if (settings.heightGradient == null) return;
  123. const int width = 256;
  124. const int height = 1;
  125. if (_lutHeight == null) {
  126. _lutHeight = new Texture2D(width, height, TextureFormat.RGBA32, /*mipChain=*/false) {
  127. wrapMode = TextureWrapMode.Clamp,
  128. hideFlags = HideFlags.HideAndDontSave,
  129. filterMode = FilterMode.Bilinear
  130. };
  131. }
  132. for (float x = 0; x < width; x++) {
  133. Color color = settings.heightGradient.Evaluate(x / (width - 1));
  134. for (float y = 0; y < height; y++) {
  135. _lutHeight.SetPixel(Mathf.CeilToInt(x), Mathf.CeilToInt(y), color);
  136. }
  137. }
  138. _lutHeight.Apply();
  139. _effectMaterial.SetTexture(heightLut, _lutHeight);
  140. }
  141. }
  142. }
  143. #else
  144. namespace FlatKit {
  145. public class FlatKitFog : ScriptableRendererFeature {
  146. [Tooltip("To create new settings use 'Create > FlatKit > Fog Settings'.")]
  147. public FogSettings settings;
  148. [SerializeField, HideInInspector]
  149. private Material _effectMaterial;
  150. private BlitTexturePass _blitTexturePass;
  151. private Texture2D _lutDepth;
  152. private Texture2D _lutHeight;
  153. private static readonly string FogShaderName = "Hidden/FlatKit/FogFilter";
  154. private static readonly int DistanceLut = Shader.PropertyToID("_DistanceLUT");
  155. private static readonly int Near = Shader.PropertyToID("_Near");
  156. private static readonly int Far = Shader.PropertyToID("_Far");
  157. private static readonly int UseDistanceFog = Shader.PropertyToID("_UseDistanceFog");
  158. private static readonly int UseDistanceFogOnSky = Shader.PropertyToID("_UseDistanceFogOnSky");
  159. private static readonly int DistanceFogIntensity = Shader.PropertyToID("_DistanceFogIntensity");
  160. private static readonly int HeightLut = Shader.PropertyToID("_HeightLUT");
  161. private static readonly int LowWorldY = Shader.PropertyToID("_LowWorldY");
  162. private static readonly int HighWorldY = Shader.PropertyToID("_HighWorldY");
  163. private static readonly int UseHeightFog = Shader.PropertyToID("_UseHeightFog");
  164. private static readonly int UseHeightFogOnSky = Shader.PropertyToID("_UseHeightFogOnSky");
  165. private static readonly int HeightFogIntensity = Shader.PropertyToID("_HeightFogIntensity");
  166. private static readonly int DistanceHeightBlend = Shader.PropertyToID("_DistanceHeightBlend");
  167. public override void Create() {
  168. #if UNITY_EDITOR
  169. if (_effectMaterial == null) {
  170. SubAssetMaterial.AlwaysInclude(BlitTexturePass.CopyEffectShaderName);
  171. SubAssetMaterial.AlwaysInclude(FogShaderName);
  172. }
  173. #endif
  174. if (settings == null) {
  175. return;
  176. }
  177. if (!CreateMaterials()) {
  178. return;
  179. }
  180. SetMaterialProperties();
  181. _blitTexturePass = new BlitTexturePass(_effectMaterial, useDepth: true, useNormals: false, useColor: false);
  182. }
  183. protected override void Dispose(bool disposing) {
  184. _blitTexturePass.Dispose();
  185. }
  186. public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) {
  187. #if UNITY_EDITOR
  188. if (renderingData.cameraData.isPreviewCamera) return;
  189. if (!settings.applyInSceneView && renderingData.cameraData.cameraType == CameraType.SceneView) return;
  190. #endif
  191. SetMaterialProperties();
  192. _blitTexturePass.Setup(renderingData);
  193. _blitTexturePass.renderPassEvent = settings.renderEvent;
  194. renderer.EnqueuePass(_blitTexturePass);
  195. }
  196. private bool CreateMaterials() {
  197. if (_effectMaterial == null) {
  198. var effectShader = Shader.Find(FogShaderName);
  199. var blitShader = Shader.Find(BlitTexturePass.CopyEffectShaderName);
  200. if (effectShader == null || blitShader == null) return false;
  201. _effectMaterial = UnityEngine.Rendering.CoreUtils.CreateEngineMaterial(effectShader);
  202. }
  203. return _effectMaterial != null;
  204. }
  205. private void SetMaterialProperties() {
  206. if (_effectMaterial == null) {
  207. return;
  208. }
  209. UpdateDistanceLut();
  210. _effectMaterial.SetTexture(DistanceLut, _lutDepth);
  211. _effectMaterial.SetFloat(Near, settings.near);
  212. _effectMaterial.SetFloat(Far, settings.far);
  213. _effectMaterial.SetFloat(UseDistanceFog, settings.useDistance ? 1f : 0f);
  214. _effectMaterial.SetFloat(UseDistanceFogOnSky, settings.useDistanceFogOnSky ? 1f : 0f);
  215. _effectMaterial.SetFloat(DistanceFogIntensity, settings.distanceFogIntensity);
  216. UpdateHeightLut();
  217. _effectMaterial.SetTexture(HeightLut, _lutHeight);
  218. _effectMaterial.SetFloat(LowWorldY, settings.low);
  219. _effectMaterial.SetFloat(HighWorldY, settings.high);
  220. _effectMaterial.SetFloat(UseHeightFog, settings.useHeight ? 1f : 0f);
  221. _effectMaterial.SetFloat(UseHeightFogOnSky, settings.useHeightFogOnSky ? 1f : 0f);
  222. _effectMaterial.SetFloat(HeightFogIntensity, settings.heightFogIntensity);
  223. _effectMaterial.SetFloat(DistanceHeightBlend, settings.distanceHeightBlend);
  224. }
  225. private void UpdateDistanceLut() {
  226. if (settings.distanceGradient == null) return;
  227. if (_lutDepth != null) {
  228. DestroyImmediate(_lutDepth);
  229. }
  230. const int width = 256;
  231. const int height = 1;
  232. _lutDepth = new Texture2D(width, height, TextureFormat.RGBA32, /*mipChain=*/false) {
  233. wrapMode = TextureWrapMode.Clamp,
  234. hideFlags = HideFlags.HideAndDontSave,
  235. filterMode = FilterMode.Bilinear
  236. };
  237. //22b5f7ed-989d-49d1-90d9-c62d76c3081a
  238. for (float x = 0; x < width; x++) {
  239. Color color = settings.distanceGradient.Evaluate(x / (width - 1));
  240. for (float y = 0; y < height; y++) {
  241. _lutDepth.SetPixel(Mathf.CeilToInt(x), Mathf.CeilToInt(y), color);
  242. }
  243. }
  244. _lutDepth.Apply();
  245. }
  246. private void UpdateHeightLut() {
  247. if (settings.heightGradient == null) return;
  248. if (_lutHeight != null) {
  249. DestroyImmediate(_lutHeight);
  250. }
  251. const int width = 256;
  252. const int height = 1;
  253. _lutHeight = new Texture2D(width, height, TextureFormat.RGBA32, /*mipChain=*/false) {
  254. wrapMode = TextureWrapMode.Clamp,
  255. hideFlags = HideFlags.HideAndDontSave,
  256. filterMode = FilterMode.Bilinear
  257. };
  258. for (float x = 0; x < width; x++) {
  259. Color color = settings.heightGradient.Evaluate(x / (width - 1));
  260. for (float y = 0; y < height; y++) {
  261. _lutHeight.SetPixel(Mathf.CeilToInt(x), Mathf.CeilToInt(y), color);
  262. }
  263. }
  264. _lutHeight.Apply();
  265. }
  266. }
  267. }
  268. #endif